package org.freeone.learning.springbootsecurity.config;

import org.freeone.learning.springbootsecurity.security.JwtAuthenticationFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;

/**
 * spring security的配置类
 */
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private static final Logger log = LoggerFactory.getLogger(SecurityConfig.class);

    @Autowired
    private UserDetailsService userDetailsService;

    private final String loginPage = "/login.htm";

    /**
     * 认证部分
     *
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 当使用postman请求时，会由于存在缓存，比如header 中修改了参数，但是仍第一次请求的值，比如下方的【authenticationEntryPoint】的request的请求头就是如此
        http.requestCache().disable();
        // 自定义登陆页面
        http.formLogin()
                // 登陆页面设置,如果加了authenticationEntryPoint就可以屏蔽下面这句话了
//                .loginPage("/login.htm")
                // 登陆访问路径
                .loginProcessingUrl("/loginProcessingUrl")
                .successForwardUrl("/index")
                // 请求转发
                .failureForwardUrl("/loginFail");

        http.headers();

        // 设置路径不需要验证
        http.authorizeRequests()
                .antMatchers("/login.htm","/login.json").permitAll()
                // 除此之外都必须验证
                .anyRequest().authenticated();
        http.logout().logoutUrl("/logout").logoutSuccessUrl("/login.htm").deleteCookies("JESSIONID").permitAll();
        http.csrf().disable().cors().and().headers().frameOptions().disable();
        // 登陆报异常，这里
        http.exceptionHandling()
                // 没有认证的
                .authenticationEntryPoint(new AuthenticationEntryPoint() {
                    @Override
                    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
                        System.out.println(request);
                        // 更稳一点就校验一下类
                        if (authException instanceof InsufficientAuthenticationException) {
                            System.out.println(authException.getClass());
//                            String token = request.getHeader("Authorization");
                            Enumeration<String> headerNames = request.getHeaderNames();
                            while (headerNames.hasMoreElements()) {
                                String name = headerNames.nextElement().toString();
                                String header = request.getHeader(name);
                                log.info("【header==】 {} : {}", name, header);
                            }
                            String clientRequestType = request.getHeader("client-request-type");
                            if (clientRequestType == null) {
                                // 重定向还是请求转发，可以在此处控制
                                response.sendRedirect(loginPage);
                            } else {
                                // 可以当成传token就是从接口请求的，返回json，而不是跳转
                                response.setContentType("application/json;charset=utf-8");
                                PrintWriter out = response.getWriter();
                                out.write("{\"code\":0,\"msg\":\"LOGIN FIRST\",\"success\":false}");
                                out.flush();
                                out.close();
                            }
                        } else {
                            System.err.println(authException.getClass());
                        }

                    }
                });

        http.addFilterBefore(new JwtAuthenticationFilter(authenticationManager()), UsernamePasswordAuthenticationFilter.class);
        //注意，这里的禁用是指Spring Security不采用session机制了，不代表你禁用掉了整个系统的session功能。
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        //解决静态资源被拦截的问题
        web.ignoring().antMatchers("/css/**", "/js/**", "/images/**").antMatchers("/login/**");
        //解决服务注册url被拦截的问题
        web.ignoring().antMatchers("/resources/**");
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManager() throws Exception {
        return super.authenticationManager();
    }
}
